package com.ruoqing.admin.aspect;

import com.ruoqing.admin.feign.AsyncLogServiceFeign;
import com.ruoqing.core.domain.rqlog.LogEntity;
import com.ruoqing.core.dto.auth.LoginUserDto;
import com.ruoqing.log.annotation.BaseLog;
import com.ruoqing.log.enums.BusinessStatus;
import com.ruoqing.redis.dto.UserAuthorityDto;
import com.ruoqing.utils.common.DateUtils;
import com.ruoqing.utils.common.IpUtils;
import com.ruoqing.utils.common.ServletUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
	* @author YaoXian
	* @title: BaseLogAspect
	* @projectName ruoxi-blog
	* @description: 若兮微服务 - 日志收集中心 - 日志记录切面
	* @date 2020-12-06 18:27
	*/

@Slf4j
@Aspect
@Component
public class BaseLogAspect {

		@Resource
		private AsyncLogServiceFeign asyncLogServiceFeign;
		
		@Resource
		private RedisTemplate<String, UserAuthorityDto> redisTemplate;

		/**
			* 配置织入点
			*/
		@Pointcut("@annotation(com.ruoqing.log.annotation.BaseLog)")
		public void logPointCut() {
		}

		/**
			* 后置通知 : 正常执行完请求后记录日志
			* @param joinPoint
			*/
		@AfterReturning(pointcut = "logPointCut()")
		public void doAfterReturning(JoinPoint joinPoint) {
				handleLog(joinPoint,null);
		}

		/**
			* 后置通知 : 拦截异常记录日志
			* @param joinPoint
			* @param e
			*/
		@AfterThrowing(value = "logPointCut()", throwing = "e")
		public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
				handleLog(joinPoint, e);
		}

		/**
			* 异步记录日志业务实现
			* @param joinPoint
			* @param e
			*/
		@Async
		protected void handleLog(final JoinPoint joinPoint, final Exception e){

				try {
						BaseLog controllerLog = getAnnotationLog(joinPoint);
						if (controllerLog == null) {
								return;
						}
						LogEntity logEntity = new LogEntity();
						logEntity.setLogStatus(BusinessStatus.SUCCESS.toString());
						logEntity.setLogOperatorIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
						logEntity.setLogOperatorUrl(ServletUtils.getRequest().getRequestURI());
						if (e!=null){
								logEntity.setLogStatus(BusinessStatus.FAIL.toString());
						}
						String className = joinPoint.getTarget().getClass().getName();
						String methodName = joinPoint.getSignature().getName();
						logEntity.setLogMethod(className + "." + methodName + "()");
						logEntity.setLogRequestMethod(ServletUtils.getRequest().getMethod());
						getControllerMethodDescription(joinPoint, controllerLog, logEntity);
						logEntity.setLogOperatorTime(DateUtils.getNowTimeCst());
						HttpServletRequest request = ServletUtils.getRequest();
						String tokenHeader = request.getHeader("Authorization");
						if (StringUtils.isEmpty(tokenHeader)){
								Object[] args = joinPoint.getArgs();
								LoginUserDto loginUserDto = (LoginUserDto) args[0];
								UserAuthorityDto userAuthorityDto = redisTemplate.boundValueOps
												(loginUserDto.getUsername()).get();
								logEntity.setLogDeptName(com.ruoqing.utils.common.StringUtils.mapToString(userAuthorityDto.getRoleMap()));
								logEntity.setLogOperatorName(loginUserDto.getUsername());
						}else {
								String[] tokenStrings = tokenHeader.split("Bearer ");
								String token = tokenStrings[1];
								UserAuthorityDto userAuthorityDto = redisTemplate.boundValueOps(token).get();
								logEntity.setLogDeptName(com.ruoqing.utils.common.StringUtils.mapToString(userAuthorityDto.getRoleMap()));
								logEntity.setLogOperatorName(userAuthorityDto.getAuthUser().getUsername());
						}
						asyncLogServiceFeign.asyncSaveLog(logEntity);
				}catch (Exception exp){
						log.error("日志收集异常!");
						exp.printStackTrace();
				}
		}

		/**
			* 是否存在注解，如果存在就获取
			*/
		private BaseLog getAnnotationLog(JoinPoint joinPoint) {
				Signature signature = joinPoint.getSignature();
				MethodSignature methodSignature = (MethodSignature) signature;
				Method method = methodSignature.getMethod();

				if (method != null)
				{
						return method.getAnnotation(BaseLog.class);
				}
				return null;
		}
		
		/**
			* 获取注解后,获取注解上的参数
			* @param joinPoint
			* @param baseLog
			* @param logEntity
			* @throws Exception
			*/
		public void getControllerMethodDescription(JoinPoint joinPoint, BaseLog baseLog, LogEntity logEntity) throws Exception{
				logEntity.setLogBusinessType(baseLog.businessType().toString());
				logEntity.setLogModule(baseLog.module());
				logEntity.setLogOperate(baseLog.title());
				logEntity.setLogOperatorType(baseLog.operatorType().toString());
		}

}
